package com.tsd.system.service.impl;

import com.tsd.core.config.session.SpringSessionContext;
import com.tsd.core.utils.HlpUtils;
import com.tsd.system.service.VerifyCodeGenService;
import com.tsd.system.utils.RandomUtils;
import com.tsd.system.vo.VerifyCodeVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;

/**
 * @ClassName: VerifyCodeGenServiceImpl
 * @Description: TODO
 * @Author: Hillpool
 * @Date: 2021/1/8 15:31
 * @Version: 1.0
 */
@Service
public class VerifyCodeGenServiceImpl implements VerifyCodeGenService {

    private static final Logger LOGGER = LoggerFactory.getLogger(VerifyCodeGenServiceImpl.class);

    private static final String[] FONT_TYPES = {"\u5b8b\u4f53", "\u65b0\u5b8b\u4f53", "\u9ed1\u4f53", "\u6977\u4f53", "\u96b6\u4e66"};

    private static final int VALICATE_CODE_LENGTH = 4;

    @Override
    public VerifyCodeVo generate(HttpServletRequest request) {
        String width = request.getParameter("width");
        String height = request.getParameter("height");
        String fontSize = request.getParameter("fontSize");
        String line = request.getParameter("line");
        String type = request.getParameter("type");
        if (HlpUtils.isEmpty(width)) {
            width = "100";
        }
        if (HlpUtils.isEmpty(height)) {
            height = "31";
        }
        if (HlpUtils.isEmpty(fontSize)) {
            fontSize = "26";
        }
        if (HlpUtils.isEmpty(line)) {
            line = "8";
        }
        int widthInt = Integer.parseInt(width);
        int heightInt = Integer.parseInt(height);
        int fontSizeInt = Integer.parseInt(fontSize);
        int lineInt = Integer.parseInt(line);
        //将流的初始化放到这里就不需要手动关闭流
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            String code;
            if (!HlpUtils.isEmpty(type) && "number".equals(type)) {
                code = generateNumber(widthInt, heightInt, fontSizeInt, lineInt, baos);
            } else {
                code = generateSting(widthInt, heightInt, fontSizeInt, lineInt, baos);
            }
            VerifyCodeVo verifyCode = new VerifyCodeVo();
            verifyCode.setCode(code);
            verifyCode.setImgBytes(baos.toByteArray());
            //将VerifyCode绑定session
            HttpSession session = request.getSession();
            session.setAttribute("VerifyCode", code);
            SpringSessionContext.addSession(session);
            verifyCode.setSessionId(session.getId());
            LOGGER.info("生成验证码：sessionId：{}, VerifyCode：{}", session.getId(), code);
            return verifyCode;
        } catch (IOException e) {
            LOGGER.error(e.getMessage(), e);
        }
        return null;
    }

    @Override
    public boolean verify(String code, HttpServletRequest request) {
        if (!HlpUtils.isEmpty(code)) {
            HttpSession session = request.getSession();
            String sessionVerifyCode = (String) session.getAttribute("VerifyCode");
            LOGGER.info("验证验证码：sessionId：{}, SessionCode：{}, VerifyCode：{}", session.getId(), sessionVerifyCode, code);
            return code.equalsIgnoreCase(sessionVerifyCode);
        }
        return false;
    }

    @Override
    public boolean verify(String code, String sessionId) {
        HttpSession session = SpringSessionContext.getSession(sessionId);
        if (session != null) {
            String sessionVerifyCode = (String) session.getAttribute("VerifyCode");
            LOGGER.info("验证验证码：sessionId：{}, SessionCode：{}, VerifyCode：{}", session.getId(), sessionVerifyCode, code);
            return code.equalsIgnoreCase(sessionVerifyCode);
        }
        return false;
    }

    /**
     * 设置背景颜色及大小，干扰线
     *
     * @param graphics
     * @param width
     * @param height
     */
    private static void fillBackground(Graphics graphics, int width, int height, int line) {
        // 填充背景
        graphics.setColor(Color.WHITE);
        //设置矩形坐标x y 为0
        graphics.fillRect(0, 0, width, height);
        // 加入干扰线条
        for (int i = 0; i < line; i++) {
            //设置随机颜色算法参数
            graphics.setColor(RandomUtils.randomColor(40, 150));
            Random random = new Random();
            int x = random.nextInt(width);
            int y = random.nextInt(height);
            int x1 = random.nextInt(width);
            int y1 = random.nextInt(height);
            graphics.drawLine(x, y, x1, y1);
        }
    }

    /**
     * 生成验证码并返回code，将图片写的os中
     *
     * @param width
     * @param height
     * @param os
     * @return
     * @throws IOException
     */
    private String generateSting(int width, int height, int fontSize, int line, OutputStream os) throws IOException {
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics graphics = image.getGraphics();
        fillBackground(graphics, width, height, line);
        String randomStr = RandomUtils.randomString(VALICATE_CODE_LENGTH);
        createCharacter(graphics, width, height, fontSize, randomStr);
        graphics.dispose();
        //设置JPEG格式
        ImageIO.write(image, "JPEG", os);
        return randomStr;
    }

    /**
     * 生成验证码并返回code，将图片写的os中
     *
     * @param width
     * @param height
     * @param os
     * @return
     * @throws IOException
     */
    private String generateNumber(int width, int height, int fontSize, int line, OutputStream os) throws IOException {
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics graphics = image.getGraphics();
        fillBackground(graphics, width, height, line);
        String randomStr = RandomUtils.randomNumberString(VALICATE_CODE_LENGTH);
        createCharacter(graphics, width, height, fontSize, randomStr);
        graphics.dispose();
        //设置JPEG格式
        ImageIO.write(image, "JPEG", os);
        return randomStr;
    }

    /**
     * 设置字符颜色大小
     *
     * @param g
     * @param randomStr
     */
    private void createCharacter(Graphics g, int width, int height, int fontSize, String randomStr) {
        char[] charArray = randomStr.toCharArray();
        int perWidth = width / charArray.length;
        for (int i = 0; i < charArray.length; i++) {
            //设置RGB颜色算法参数
            g.setColor(new Color(50 + RandomUtils.nextInt(100), 50 + RandomUtils.nextInt(100), 50 + RandomUtils.nextInt(100)));
            //设置字体大小，类型
            g.setFont(new Font(FONT_TYPES[RandomUtils.nextInt(FONT_TYPES.length)], Font.BOLD, fontSize));
            FontMetrics metrics = g.getFontMetrics();
            int fontWidth = metrics.stringWidth(String.valueOf(charArray[i]));
            int fontHeight = metrics.getAscent() + metrics.getDescent();
            int pos = (perWidth - fontWidth) / 2;
            if (i == 0 && pos < 0) {
                pos = 0;
            }
            int x = perWidth * i + pos;
            if (x + fontWidth > width) {
                x = width - fontWidth;
            }
            int perHeight = (height / 2) + metrics.getAscent() - (fontHeight / 2);
            int randomNum = RandomUtils.nextInt(fontHeight / 2);
            int ranInt = Math.random() > 0.5 ? 0 : 1;
            if (ranInt == 0) {
                randomNum = -randomNum;
            }
            int y = perHeight + randomNum;
            if (y - metrics.getAscent() < 0) {
                y = metrics.getAscent();
            }
            if (y + metrics.getDescent() > height) {
                y = height - metrics.getDescent();
            }
            //设置x y 坐标
            g.drawString(String.valueOf(charArray[i]), x, y);
        }
    }
}
